package com.blb.zycommunity.config;

import com.blb.zycommunity.entity.ResponseResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Arrays;

/**
 * SpringSecurity整合MVC配置，继承WebSecurityConfigurerAdapter
 * Adapter适配器设计模式，实现接口必须实现接口中所有方法
 * Adapter给接口方法默认实现，继承Adapter后只需要实现特定方法
 */
//启动注解配置授权访问
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    //提供密码编码器给容器
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Resource
    private UserDetailsService userDetailsService;


    //配置验证的账号密码
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //配置自定义的登录逻辑
        auth.userDetailsService(userDetailsService);
    }

    //放行图片上传方法
    @Override
    public void configure(WebSecurity web) {
        web.ignoring().antMatchers("/**/upload/*","/visitor","/interactions","/comments/**","/comment","/interaction","/likeNuma", "/likeNumb", "/liked", "/liked-list","/weixiupload","/visitors"
                ,"/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs","/owner-add","/uni-app-login");
    }

    @Autowired
    private DataSource dataSource;

    @Autowired
    private PersistentTokenRepository persistentTokenRepository;

    //配置访问授权
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //配置资源的授权访问
        http.authorizeRequests().antMatchers("/login","/logout","/uni-app-logout").permitAll() //上面地址放行
                .and()
                .formLogin().loginProcessingUrl("/login")
                .successHandler(new LoginSuccessHandler())  //配置登录成功处理器
                .failureHandler((request,response,chain) ->{//配置登陆失败处理器
                    //向前端发送错误数据
                    ResponseResult.write(ResponseResult.error(401L,"该用户不存在或已被禁用"),response);
                })
                .and()
                .exceptionHandling()
                .authenticationEntryPoint((request,response,chain)->{//配置权限认证失败处理
                    //向前端发送错误数据
                    ResponseResult.write(ResponseResult.error(403L,"用户名或密码错误"),response);
                })
                .and()
                .rememberMe()
                .rememberMeParameter("rememberMe")
                .tokenRepository(persistentTokenRepository) //数据源
                .tokenValiditySeconds(7 * 24 * 3600) //时间 秒
                .and()
                .logout()
                .logoutSuccessHandler((request,response,chain)->{//配置注销成功处理器
                    //向前端发送错误数据
                    ResponseResult.write(ResponseResult.ok("注销成功"),response);
                })
                .clearAuthentication(true)      //清除服务器验证信息
                .and()
                .csrf().disable()    //停止csrf攻击保护
                .sessionManagement()        //session管理
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .cors()
                //配置跨域
                .configurationSource(corsConfigurationSource())
                .and()
                //在过滤器链末尾添加自定义验证过滤器
                .addFilter(new TokenAuthenticationFilter(authenticationManager()));
                //记住我配置

    }

    /**
     * 配置记住我的数据源
     * @return
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        //使用jdbc实现记住我
        JdbcTokenRepositoryImpl repository = new JdbcTokenRepositoryImpl();
        //配置数据源
        repository.setDataSource(dataSource);
        //第一次启动时创建表，后面不能创建
        return repository;
    }

    /**
     * 跨域配置对象
     */
    @Bean
    public CorsConfigurationSource corsConfigurationSource(){
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        //配置允许访问的服务器域名
        corsConfiguration.setAllowedOrigins(Arrays.asList("*"));
        corsConfiguration.setAllowedMethods(Arrays.asList("GET","POST","PUT","DELETE"));
        corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
        corsConfiguration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**",corsConfiguration);
        return source;
    }
}